/*
 * Copyright 2010 Electronic Business Systems Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * http://www.apache.org/licenses/LICENSE-2.0
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package gr.ebs.gssdroid.http;

import gr.ebs.gssdroid.GssDroid;
import gr.ebs.gssdroid.async.FileMonitor;
import gr.ebs.gssdroid.async.GssAsyncTask;
import gr.ebs.gssdroid.resource.FileResource;
import gr.ebs.gssdroid.resource.FolderResource;
import gr.ebs.gssdroid.resource.GroupResource;
import gr.ebs.gssdroid.resource.GroupUserResource;
import gr.ebs.gssdroid.resource.GroupsResource;
import gr.ebs.gssdroid.resource.OtherUserResource;
import gr.ebs.gssdroid.resource.OthersResource;
import gr.ebs.gssdroid.resource.SearchResource;
import gr.ebs.gssdroid.resource.SharedResource;
import gr.ebs.gssdroid.resource.TrashResource;
import gr.ebs.gssdroid.resource.UserResource;
import gr.ebs.gssdroid.resource.UserSearchResource;
import gr.ebs.gssdroid.util.UserDetails;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.URLEncoder;

import org.apache.commons.io.IOUtils;
import org.apache.http.Header;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.json.JSONException;

import android.os.Environment;
import android.util.Log;

public class GssHttpCommands {
	private static final int DEFAULT_BUFFER_SIZE = 1024 * 4;
	private UserDetails details;
	private GssHttpClient client;
	public GssHttpCommands(UserDetails details) {
		this.details = details;
		this.client = new GssHttpClient(details);
	}
	public UserResource getUserResource(String path) throws SystemErrorException,GssHttpException{
		path=path+"/";
		HttpResponse response = client.executeGet(path);
		Log.d("GssHTTPCommand","Execute for "+path+" returned:"+response.getStatusLine().getStatusCode());
		if(response.getStatusLine().getStatusCode()==HttpStatus.SC_OK){
			UserResource result = new UserResource(path);
			try {
				result.createFromJSON(getContent(response));
			} catch (Exception e) {
				throw new SystemErrorException("unable to parse json response", e);
			} 
			return result;
		}
		throw new GssHttpException("error",response.getStatusLine().getStatusCode());
	}
	public FolderResource getFolderResource(String path) throws SystemErrorException,GssHttpException{
		HttpResponse response = client.executeGet(path);
		Log.d("GssHTTPCommand","Execute for "+path+" returned:"+response.getStatusLine().getStatusCode());
		if(response.getStatusLine().getStatusCode()==HttpStatus.SC_OK){
			FolderResource result = new FolderResource(path);
			try {
				result.createFromJSON(getContent(response));
			} catch (Exception e) {
				Log.e("GSSHTTPERROR:","json parse:",e);
				throw new SystemErrorException("unable to parse json response", e);
			} 
			return result;
		}
		throw new GssHttpException("error",response.getStatusLine().getStatusCode());
	}
	
	public TrashResource getTrashResource(String path) throws SystemErrorException,GssHttpException{
		HttpResponse response = client.executeGet(path);
		Log.d("GssHTTPCommand","Execute for "+path+" returned:"+response.getStatusLine().getStatusCode());
		if(response.getStatusLine().getStatusCode()==HttpStatus.SC_OK){
			TrashResource result = new TrashResource(path);
			try {
				result.createFromJSON(getContent(response));
			} catch (Exception e) {
				throw new SystemErrorException("unable to parse json response", e);
			} 
			return result;
		}
		else if(response.getStatusLine().getStatusCode()==HttpStatus.SC_NO_CONTENT){
			TrashResource result = new TrashResource(path);
			return result;
		}
		throw new GssHttpException("error",response.getStatusLine().getStatusCode());
	}
	
	public SharedResource getSharedResource(String path) throws SystemErrorException,GssHttpException{
		HttpResponse response = client.executeGet(path);
		Log.d("GssHTTPCommand","Execute for "+path+" returned:"+response.getStatusLine().getStatusCode());
		if(response.getStatusLine().getStatusCode()==HttpStatus.SC_OK){
			SharedResource result = new SharedResource(path);
			try {
				result.createFromJSON(getContent(response));
			} catch (Exception e) {
				throw new SystemErrorException("unable to parse json response", e);
			} 
			return result;
		}
		throw new GssHttpException("error",response.getStatusLine().getStatusCode());
	}
	
	public OthersResource getOthersResource(String path) throws SystemErrorException,GssHttpException{
		HttpResponse response = client.executeGet(path);
		Log.d("GssHTTPCommand","Execute for "+path+" returned:"+response.getStatusLine().getStatusCode());
		if(response.getStatusLine().getStatusCode()==HttpStatus.SC_OK){
			OthersResource result = new OthersResource(path);
			try {
				result.createFromJSON(getContent(response));
			} catch (Exception e) {
				throw new SystemErrorException("unable to parse json response", e);
			} 
			return result;
		}
		throw new GssHttpException("error",response.getStatusLine().getStatusCode());
	}
	
	public OtherUserResource getOtherUserResource(String path) throws SystemErrorException,GssHttpException{
		HttpResponse response = client.executeGet(path);
		Log.d("GssHTTPCommand","Execute for "+path+" returned:"+response.getStatusLine().getStatusCode());
		if(response.getStatusLine().getStatusCode()==HttpStatus.SC_OK){
			OtherUserResource result = new OtherUserResource(path);
			try {
				result.createFromJSON(getContent(response));
			} catch (Exception e) {
				throw new SystemErrorException("unable to parse json response", e);
			} 
			return result;
		}
		throw new GssHttpException("error",response.getStatusLine().getStatusCode());
	}
	
	public FileResource getFileResource(String path) throws SystemErrorException, GssHttpException{
		HttpResponse response = client.executeHead(path);
		Log.d("GssHTTPCommand","Execute for "+path+" returned:"+response.getStatusLine().getStatusCode());
		if(response.getStatusLine().getStatusCode()==HttpStatus.SC_OK){
			Header[] headers = response.getHeaders("X-GSS-Metadata");
			StringBuilder json = new StringBuilder();
			if(headers.length>0){
				json.append(headers[0].getValue());
			}
			FileResource result = new FileResource(path);
			try {
				result.createFromJSON(json.toString());
			} catch (Exception e) {
				throw new SystemErrorException("unable to parse json response", e);
			} 
			return result;
		}
		throw new GssHttpException("error",response.getStatusLine().getStatusCode());
	}
	
	public void createNewFolder(String path, String folderName) throws SystemErrorException,GssHttpException{
		String pathToExecute=path+"?new="+URLEncoder.encode(folderName);
		HttpResponse response = client.executePost(pathToExecute,null);
		Log.d("GssHTTPCommand","Execute for "+path+" returned:"+response.getStatusLine().getStatusCode());
		if(response.getStatusLine().getStatusCode()!=HttpStatus.SC_CREATED){
			Header[] headers = response.getHeaders("Allow");
			if(headers.length>0)
				throw new GssHttpException("error",response.getStatusLine().getStatusCode(), headers[0].getValue());
			throw new GssHttpException("error",response.getStatusLine().getStatusCode());
		}
		
		
	}
	
	public void updateFolder(String path, String data) throws SystemErrorException,GssHttpException{
		String pathToExecute=path+"?update=";
		HttpResponse response = client.executePost(pathToExecute,data);
		Log.d("GssHTTPCommand","Execute for "+path+" returned:"+response.getStatusLine().getStatusCode());
		if(response.getStatusLine().getStatusCode()!=HttpStatus.SC_OK){
			Header[] headers = response.getHeaders("Allow");
			if(headers.length>0)
				throw new GssHttpException("error",response.getStatusLine().getStatusCode(), headers[0].getValue());
			throw new GssHttpException("error",response.getStatusLine().getStatusCode());
		}
		
	}
	
	public void copy(String source, String target) throws SystemErrorException,GssHttpException{
		String pathToExecute=source+"?copy="+target;
		HttpResponse response = client.executePost(pathToExecute,null);
		Log.d("GssHTTPCommand","Execute for "+pathToExecute+" returned:"+response.getStatusLine().getStatusCode());
		if(response.getStatusLine().getStatusCode()!=HttpStatus.SC_OK){
			Header[] headers = response.getHeaders("Allow");
			if(headers.length>0)
				throw new GssHttpException("error",response.getStatusLine().getStatusCode(), headers[0].getValue());
			throw new GssHttpException("error",response.getStatusLine().getStatusCode());
		}
		
	}
	
	public void move(String source, String target) throws SystemErrorException,GssHttpException{
		String pathToExecute=source+"?move="+target;
		HttpResponse response = client.executePost(pathToExecute,null);
		Log.d("GssHTTPCommand","Execute for "+pathToExecute+" returned:"+response.getStatusLine().getStatusCode());
		if(response.getStatusLine().getStatusCode()!=HttpStatus.SC_OK){
			Header[] headers = response.getHeaders("Allow");
			if(headers.length>0)
				throw new GssHttpException("error",response.getStatusLine().getStatusCode(), headers[0].getValue());
			throw new GssHttpException("error",response.getStatusLine().getStatusCode());
		}
		
	}
	
	public void updateFile(String path, String data) throws SystemErrorException,GssHttpException{
		String pathToExecute=path+"?update=";
		HttpResponse response = client.executePost(pathToExecute,data);
		Log.d("GssHTTPCommand","Execute for "+pathToExecute+" returned:"+response.getStatusLine().getStatusCode()+" for data:"+data);
		if(response.getStatusLine().getStatusCode()!=HttpStatus.SC_OK){
			throw new GssHttpException("error",response.getStatusLine().getStatusCode());
		}
		
	}
	

	public void createNewGroup(String path, String groupName) throws SystemErrorException, GssHttpException {
		if(!path.endsWith("/"))
			path=path+"/";
		String pathToExecute=path+"?name="+groupName;
		HttpResponse response = client.executePost(pathToExecute,null);
		Log.d("GssHTTPCommand","Execute for "+pathToExecute+" returned:"+response.getStatusLine().getStatusCode());
		if(response.getStatusLine().getStatusCode()!=HttpStatus.SC_CREATED){
			throw new GssHttpException("error",response.getStatusLine().getStatusCode());
		}
		
	}
	
	public void moveFolderToTrash(String path) throws SystemErrorException,GssHttpException{
		String pathToExecute=path+"?trash=";
		HttpResponse response = client.executePost(pathToExecute,null);
		Log.d("GssHTTPCommand","Execute for "+path+" returned:"+response.getStatusLine().getStatusCode());
		if(response.getStatusLine().getStatusCode()!=HttpStatus.SC_OK){
			throw new GssHttpException("error",response.getStatusLine().getStatusCode());
		}
		
	}
	
	public void restoreFolderFromTrash(String path) throws SystemErrorException,GssHttpException{
		String pathToExecute=path+"?restore=";
		HttpResponse response = client.executePost(pathToExecute,null);
		Log.d("GssHTTPCommand","Execute for "+path+" returned:"+response.getStatusLine().getStatusCode());
		if(response.getStatusLine().getStatusCode()!=HttpStatus.SC_OK){
			throw new GssHttpException("error",response.getStatusLine().getStatusCode());
		}
	}
	
	public void deleteFolder(String path) throws SystemErrorException, GssHttpException{
		HttpResponse response = client.executeDelete(path);
		Log.d("GssHTTPCommand","Execute for "+path+" returned:"+response.getStatusLine().getStatusCode());
		if(response.getStatusLine().getStatusCode()!=HttpStatus.SC_NO_CONTENT){
			throw new GssHttpException("error",response.getStatusLine().getStatusCode());
		}
	}
	
	public void uploadFile(String path, String uri, GssAsyncTask task, int number) throws SystemErrorException,GssHttpException{
		String filename=uri;
		if (filename.startsWith("file://")) {
            filename = filename.substring(7);
        }
		HttpResponse response = client.executePut(path, filename, task, number);
		Log.d("GssHTTPCommand","Execute for "+path+" returned:"+response.getStatusLine().getStatusCode());
		if(response.getStatusLine().getStatusCode()!=HttpStatus.SC_CREATED&&response.getStatusLine().getStatusCode()!=HttpStatus.SC_NO_CONTENT){
			throw new GssHttpException("error",response.getStatusLine().getStatusCode());
		}
	}
	
	public String downloadFileCommand(String path, GssAsyncTask task) throws SystemErrorException,GssHttpException{
		HttpResponse response = client.executeGet(path);
		StringBuilder json = new StringBuilder();
		Log.d("GssHTTPCommand","Execute for "+path+" returned:"+response.getStatusLine().getStatusCode());
		if(response.getStatusLine().getStatusCode()!=HttpStatus.SC_OK){
			throw new GssHttpException("error",response.getStatusLine().getStatusCode());
		}
		Header[] headers = response.getHeaders("X-GSS-Metadata");
		Log.d("HEAD",path+" : "+ headers.length);
		if(headers.length>0){
			Log.d("HEAD",path+" : "+ headers[0].getValue());
		
			json.append(headers[0].getValue());
		}
		else
			return null;
		FileResource result = new FileResource(path);
		try {
			result.createFromJSON( json.toString());
		} catch (JSONException e1) {
			Log.e("DOWNLOAD", "sk",e1);
			throw new GssHttpException("Unable to parse json");
			
		}
		try {
		    File root = Environment.getExternalStorageDirectory();
		    if (root.canWrite()){
		    	File gss = new File(root,"gss");
		    	gss.mkdir();
		    	long startTime = System.currentTimeMillis();
		        File gpxfile = new File(gss, result.getName());
		        InputStream is = response.getEntity().getContent();
		        //IOUtils.copy(is, new FileOutputStream(gpxfile));
		        copy(is, new FileOutputStream(gpxfile), new FileMonitor(result.getContentLength(), task));
		        IOUtils.closeQuietly(is);
                Log.d("ImageManager", "download ready in"
                                + ((System.currentTimeMillis() - startTime) / 1000)
                                + " sec");

        
		        return gpxfile.getPath();
		    }
		} catch (IOException e) {
			Log.e("DOWNLOAD", "sk",e);
		    throw new SystemErrorException("unable to write file",e);
		}
		return null;
	}
	/*for(String pg : groups.getGroupPaths()){
					GroupResource g = http.getGroupResource(pg);
					for(String pu : g.getUserPaths()){
						GroupUserResource u = http.getGroupUserResource(pu);
						g.getUsers().add(u);
					}
					groups.getGroups().add(g);
				}*/
	
	public GroupsResource getGroupsResourceExpanded(String path) throws SystemErrorException,GssHttpException{
		HttpResponse response = client.executeGet(path);
		Log.d("GssHTTPCommand","Execute for "+path+" returned:"+response.getStatusLine().getStatusCode());
		if(response.getStatusLine().getStatusCode()==HttpStatus.SC_OK){
			GroupsResource result = new GroupsResource(path);
			try {
				result.createFromJSON(getContent(response));
				for(String pg : result.getGroupPaths()){
					GroupResource g = getGroupResource(pg);
					for(String pu : g.getUserPaths()){
						GroupUserResource u = getGroupUserResource(pu);
						g.getUsers().add(u);
					}
					result.getGroups().add(g);
				}
			} catch (Exception e) {
				throw new SystemErrorException("unable to parse json response", e);
			} 
			return result;
		}
		throw new GssHttpException("error",response.getStatusLine().getStatusCode());
	}
	
	public GroupsResource getGroupsResource(String path) throws SystemErrorException,GssHttpException{
		HttpResponse response = client.executeGet(path);
		Log.d("GssHTTPCommand","Execute for "+path+" returned:"+response.getStatusLine().getStatusCode());
		if(response.getStatusLine().getStatusCode()==HttpStatus.SC_OK){
			GroupsResource result = new GroupsResource(path);
			try {
				result.createFromJSON(getContent(response));
			} catch (Exception e) {
				throw new SystemErrorException("unable to parse json response", e);
			} 
			return result;
		}
		throw new GssHttpException("error",response.getStatusLine().getStatusCode());
	}
	
	
	
	public GroupResource getGroupResource(String path) throws SystemErrorException,GssHttpException{
		HttpResponse response = client.executeGet(path);
		Log.d("GssHTTPCommand","Execute for "+path+" returned:"+response.getStatusLine().getStatusCode());
		if(response.getStatusLine().getStatusCode()==HttpStatus.SC_OK){
			GroupResource result = new GroupResource(path);
			try {
				result.createFromJSON(getContent(response));
			} catch (Exception e) {
				throw new SystemErrorException("unable to parse json response", e);
			} 
			return result;
		}
		throw new GssHttpException("error",response.getStatusLine().getStatusCode());
	}
	
	public GroupUserResource getGroupUserResource(String path) throws SystemErrorException,GssHttpException{
		HttpResponse response = client.executeGet(path);
		Log.d("GssHTTPCommand","Execute for "+path+" returned:"+response.getStatusLine().getStatusCode());
		if(response.getStatusLine().getStatusCode()==HttpStatus.SC_OK){
			GroupUserResource result = new GroupUserResource(path);
			try {
				result.createFromJSON(getContent(response));
			} catch (Exception e) {
				throw new SystemErrorException("unable to parse json response", e);
			} 
			return result;
		}
		throw new GssHttpException("error",response.getStatusLine().getStatusCode());
	}
	
	public UserSearchResource getSearchUsers(String path) throws SystemErrorException,GssHttpException{
		HttpResponse response = client.executeGet(path);
		Log.d("GssHTTPCommand","Execute for "+path+" returned:"+response.getStatusLine().getStatusCode());
		if(response.getStatusLine().getStatusCode()==HttpStatus.SC_OK){
			UserSearchResource result = new UserSearchResource(path);
			try {
				result.createFromJSON(getContent(response));
			} catch (Exception e) {
				throw new SystemErrorException("unable to parse json response", e);
			} 
			return result;
		}
		throw new GssHttpException("error",response.getStatusLine().getStatusCode());
	}
	
	private String getContent(HttpResponse response) throws IOException{
		StringBuilder json = new StringBuilder();
		BufferedReader reader = new BufferedReader(new InputStreamReader(response.getEntity().getContent()));
		String line = reader.readLine();
		while (line != null) {
			json.append(line);
			line = reader.readLine();
		}
		return json.toString();
	}
	
	public static long copy(InputStream input, OutputStream output, FileMonitor monitor)
                throws IOException {
        byte[] buffer = new byte[DEFAULT_BUFFER_SIZE];
        long count = 0;
        int n = 0;
        while (-1 != (n = input.read(buffer))) {
            output.write(buffer, 0, n);
            count += n;
            monitor.setCurrent(count);
        }
        return count;
    }

	public String restoreVersion(String path, GssAsyncTask gssAsyncTask) throws GssHttpException, SystemErrorException {
		HttpResponse response = client.executePost(path,null);
		Log.d("GssHTTPCommand","Execute for "+path+" returned:"+response.getStatusLine().getStatusCode());
		if(response.getStatusLine().getStatusCode()!=HttpStatus.SC_OK){
			throw new GssHttpException("error",response.getStatusLine().getStatusCode());
		}
		return path;
	}
	
	public SearchResource search( String query) throws SystemErrorException, GssHttpException{
		String path = GssDroid.BASE_REST_URL+"search/"+URLEncoder.encode(query);
		HttpResponse response = client.executeGet(path);
		Log.d("GssHTTPCommand","Execute for "+path+" returned:"+response.getStatusLine().getStatusCode());
		if(response.getStatusLine().getStatusCode()==HttpStatus.SC_OK){
			SearchResource result = new SearchResource(path);
			result.setQuery(query);
			try {
				result.createFromJSON(getContent(response));
			} catch (Exception e) {
				throw new SystemErrorException("unable to parse json response", e);
			} 
			return result;
		}
		throw new GssHttpException("error",response.getStatusLine().getStatusCode());
	}
}
